package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.heima.article.mapper.ArticleMapper;
import com.heima.article.service.HotArticleService;
import com.heima.article.service.IArticleService;
import com.heima.feign.client.ChannelFeginClient;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.message.ArticleVisitStreamMess;
import com.heima.model.wemedia.pojos.WmChannel;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 计算热点文章的业务方法
 */
@Service
public class HotArticleServiceImpl implements HotArticleService {

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private ChannelFeginClient channelFeginClient;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 计算热点文章的业务方法
     * 是否有返回值,没有
     * 是否有参数，
     * 没有，
     * 该业务方法是要被定时任务自动调用的
     */
    @Override
    public void computeHotArticle() {
        //1.查询前五天的文章列表
      List<ApArticle> articleList=last5DayArticleList();

        //2.计算分值
       List<HotArticleVo> articleScoreList= jisuanArticleScore(articleList);

        //3.计算分值之后的文章列表存储到redis中
        cacheArticleToRedis(articleScoreList);
    }


    /**
     * 抽取的第四个方法，缓存数据到redis
     * @param articleScoreList
     */
    private void cacheArticleToRedis(List<HotArticleVo> articleScoreList) {
        //1.根据每个频道来缓存
            //1.1 获取所有的频道列表
        ResponseResult<List<WmChannel>> responseResult = channelFeginClient.list();
        List<WmChannel> channels = responseResult.getData();
        for (WmChannel channel : channels) {
            //获取每一个频道下的所有的文章列表
            List<HotArticleVo> channelArticleList = articleScoreList.stream().filter(hotArticleVo -> hotArticleVo.getChannelId().equals(channel.getId())).collect(Collectors.toList());
            cacheToRedis(channelArticleList,"hot_article_first_page_"+ channel.getId());
        }
        //2.根据推荐来缓存
        cacheToRedis(articleScoreList,"hot_article_first_page"+"__all__");
    }

    private void cacheToRedis( List<HotArticleVo> articleList,String key) {
        //按照分值排序+取出前三十条分值最高的文章
        articleList = articleList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                .limit(30)
                .collect(Collectors.toList());
        //存数据到redis中
        redisTemplate.opsForValue().set(key, JSON.toJSONString(articleList));
    }

    /**
     * 第二个抽取的方法，计算每一篇的文章分值
     * @param articleList
     * @return
     */
    private List<HotArticleVo> jisuanArticleScore(List<ApArticle> articleList) {

        List<HotArticleVo> articleScoreList=new ArrayList<>();

        //1.判断非空
        if(articleList==null || articleList.size()<=0){
            return null;
        }
        //2.计算分值，每一篇文章
        for (ApArticle apArticle : articleList) {
            HotArticleVo vo=new HotArticleVo();
            BeanUtils.copyProperties(apArticle,vo);
            //调用计算分值
            Integer score=computeArticleScore(apArticle);
            vo.setScore(score);
            articleScoreList.add(vo);
        }
        return articleScoreList;
    }

    /**
     * 抽取的第三个方法，计算每一篇文章的分值
     * @return
     */
    private Integer computeArticleScore(ApArticle apArticle) {
        Integer score=0;
        if(apArticle.getLikes()!=null){
            score+=apArticle.getLikes()*3;
        }
        if(apArticle.getViews()!=null){
            score+=apArticle.getViews();
        }
        if(apArticle.getComment()!=null){
            score+=apArticle.getComment()*5;
        }
        if(apArticle.getCollection()!=null){
            score+=apArticle.getCollection()*8;
        }
        return score;
    }

    /**
     * 抽取的第一个方法，获取前五天的文章列表
     * @return
     */
    private List<ApArticle> last5DayArticleList() {
        //2.获取前五天的日期
        Calendar calendar=Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_MONTH,-5);
        Date before5Day=calendar.getTime();

        //1.调用mapper接口
        List<ApArticle> before5DayArtileList = articleMapper.findBefore5DayArtileList(before5Day);
        return before5DayArtileList;
    }


    /**
     * 实时计算热点文章
     *
     * @param mess
     */
    @Override
    public void computeHotArticleIncrHandle(ArticleVisitStreamMess mess) {
            //1.修改表中的行为数量
            if(mess.getArticleId()==null){
                return;
            }
                //1.1 根据文章id去查询文章信息
        ApArticle apArticle = articleMapper.selectById(mess.getArticleId());
                //1.2 执行修改行为数据
        //阅读
        if(mess.getView()!=0){
            apArticle.setViews((apArticle.getViews()==null?0:apArticle.getViews())+mess.getView());
        }
        //点赞
        if(mess.getLike()!=0){
            apArticle.setLikes((apArticle.getLikes()==null?0:apArticle.getLikes())+mess.getLike());
        }
        //评论
        if(mess.getComment()!=0){
            apArticle.setComment((apArticle.getComment()==null?0:apArticle.getComment())+mess.getComment());
        }
        //收藏
        if(mess.getCollect()!=0){
            apArticle.setCollection((apArticle.getCollection()==null?0:apArticle.getCollection())+mess.getCollect());
        }
        articleMapper.updateById(apArticle);

        //2.重新计算分值
        Integer score = this.computeArticleScore(apArticle);
        score=score*3;

        //3.替换redis中的数据
        //频道
        replaceHotArticleToRedis(apArticle,score,"hot_article_first_page_" + apArticle.getChannelId());
        //推荐
        replaceHotArticleToRedis(apArticle,score,"hot_article_first_page" + "__all__");

    }

    /**
     * 替换redis中的数据
     */
    private void replaceHotArticleToRedis(ApArticle apArticle,Integer score,String key) {
        //1.查询redis
        //频道
        String channelHotArticleVoStr = redisTemplate.opsForValue().get(key);
        List<HotArticleVo> hotArticleVos = JSONArray.parseArray(channelHotArticleVoStr, HotArticleVo.class);

        Boolean flag=true;


        //判断当前缓存中是否存在
        if(hotArticleVos!=null && hotArticleVos.size()>0){
            for (HotArticleVo oldVo : hotArticleVos) {
                if(oldVo.getId().equals(apArticle.getId())){
                   oldVo.setScore(score);
                    flag=false;
                    break;
                }
            }
        }
       if(flag){
           //3.封装数据
           HotArticleVo vo=new HotArticleVo();
           BeanUtils.copyProperties(apArticle,vo);
           vo.setScore(score);

           //表示缓存中不存在
           //判断当前缓存中的长度是否大于30
           if(hotArticleVos.size()>=30){
               //如果大于30，取出最后一条，然后比大小，然后如果小于最新的数据，则替换
               HotArticleVo lastHotArticleVo = hotArticleVos.get(hotArticleVos.size() - 1);
               if(lastHotArticleVo.getScore()<vo.getScore()){
                   //1.先暂时从hotArticleVos干掉lastHotArticleVo
                   hotArticleVos.remove(lastHotArticleVo);
                   //2.添加
                   hotArticleVos.add(vo);
               }

           }else{
               //如果小于30，则直接添加redis
               hotArticleVos.add(vo);
           }
       }
       //尽可能对list进行排序
        hotArticleVos = hotArticleVos.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());
        //重新赋值给redis
        redisTemplate.opsForValue().set(key,JSON.toJSONString(hotArticleVos));
    }


}
